home *** CD-ROM | disk | FTP | other *** search
- /*======================================================================
- pipe
-
- Initiate I/O to and from a process. These functions are similar to
- popen and pclose, but both an incoming stream and an output file are
- provided.
-
- ====*/
-
- #ifndef STDIN_FILENO
- #define STDIN_FILENO 0
- #endif
- #ifndef STDOUT_FILENO
- #define STDOUT_FILENO 1
- #endif
- #ifndef STDERR_FILENO
- #define STDERR_FILENO 2
- #endif
-
-
- /*
- * Defs to help fish child's exit status out of wait(2)
- */
- #ifdef HAVE_WAIT_UNION
- #define WaitType union wait
- #ifndef WIFEXITED
- #define WIFEXITED(X) (!(X).w_termsig) /* child exit by choice */
- #endif
- #ifndef WEXITSTATUS
- #define WEXITSTATUS(X) (X).w_retcode /* childs chosen exit value */
- #endif
- #else
- #define WaitType int
- #ifndef WIFEXITED
- #define WIFEXITED(X) (!((X) & 0xff)) /* low bits tell how it died */
- #endif
- #ifndef WEXITSTATUS
- #define WEXITSTATUS(X) (((X) >> 8) & 0xff) /* high bits tell exit value */
- #endif
- #endif
-
-
- /*
- * Global's to helpsignal handler tell us child's status has changed...
- */
- short child_signalled;
- short child_jump = 0;
- jmp_buf child_state;
-
-
-
- /*----------------------------------------------------------------------
- Spawn a child process and optionally connect read/write pipes to it
-
- Args: command -- string to hand the shell
- outfile -- address of pointer containing file to receive output
- errfile -- address of pointer containing file to receive error output
- mode -- mode for type of shell, signal protection etc...
- Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
-
- The outfile is either NULL, a pointer to a NULL value, or a pointer
- to the requested name for the output file. In the pointer-to-NULL case
- the caller doesn't care about the name, but wants to see the pipe's
- results so we make one up. It's up to the programmer to make sure
- the free storage containing the name is cleaned up.
-
- Mode bits serve several purposes.
- PIPE_WRITE tells us we need to open a pipe to write the child's
- stdin.
- PIPE_READ tells us we need to open a pipe to read from the child's
- stdout/stderr. *NOTE* Having neither of the above set means
- we're not setting up any pipes, just forking the child and exec'ing
- the command. Also, this takes precedence over any named outfile.
- PIPE_STDERR means we're to tie the childs stderr to the same place
- stdout is going. *NOTE* This only makes sense then if PIPE_READ
- or an outfile is provided. Also, this takes precedence over any
- named errfile.
- PIPE_PROT means to protect the child from the usual nasty signals
- that might cause premature death. Otherwise, the default signals are
- set so the child can deal with the nasty signals in its own way.
- PIPE_NOSHELL means we're to exec the command without the aid of
- a system shell. *NOTE* This negates the affect of PIPE_USER.
- PIPE_USER means we're to try executing the command in the user's
- shell. Right now we only look in the environment, but that may get
- more sophisticated later.
- PIPE_RESET means we reset the terminal mode to what it was before
- we started pine and then exec the command.
- ----*/
- PIPE_S *
- open_system_pipe(command, outfile, errfile, mode)
- char *command;
- char **outfile, **errfile;
- int mode;
- {
- PIPE_S *syspipe = NULL;
- char shellpath[32], *shell;
- int p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
-
- dprint(5, (debugfile, "Opening pipe: \"%s\" (%s%s%s%s%s%s)\n",command,
- (mode & PIPE_WRITE) ? "W":"", (mode & PIPE_READ) ? "R":"",
- (mode & PIPE_NOSHELL) ? "N":"", (mode & PIPE_PROT) ? "P":"",
- (mode & PIPE_USER) ? "U":"", (mode & PIPE_RESET) ? "T":""));
-
- if(!(mode & PIPE_READ)){
- if(outfile && !*outfile)
- *outfile = temp_nam(NULL, "pine_p"); /* asked for, but not named? */
-
- if(errfile && !*errfile)
- *errfile = temp_nam(NULL, "pine_p"); /* ditto */
- }
-
- if(mode & (PIPE_WRITE | PIPE_READ)){
- if(mode & PIPE_WRITE){
- pipe(p); /* alloc pipe to write child */
- oparentd = p[STDOUT_FILENO];
- ichildd = p[STDIN_FILENO];
- }
-
- if(mode & PIPE_READ){
- pipe(p); /* alloc pipe to read child */
- iparentd = p[STDIN_FILENO];
- ochildd = p[STDOUT_FILENO];
- }
- }
- else{
- flush_status_messages(0); /* just clean up display */
- ClearScreen();
- fflush(stdout);
- }
-
- syspipe = (PIPE_S *)fs_get(sizeof(PIPE_S));
- memset(syspipe, 0, sizeof(PIPE_S));
-
- if((syspipe->mode = mode) & PIPE_RESET)
- Raw(0);
-
- #ifdef SIGCHLD
- /*
- * Prepare for demise of child. Use SIGCHLD if it's available so
- * we can do useful things, like keep the IMAP stream alive, while
- * we're waiting on the child.
- */
- child_signalled = child_jump = 0;
- (void)signal(SIGCHLD, child_signal);
- #endif
-
- if((syspipe->pid = vfork()) == 0){
- /* reset child's handlers in requested fashion... */
- (void)signal(SIGINT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
- (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
- (void)signal(SIGHUP, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
- #ifdef SIGCHLD
- (void) signal(SIGCHLD, SIG_DFL);
- #endif
-
- /* if parent isn't reading, and we have a filename to write */
- if(!(mode & PIPE_READ) && outfile){ /* connect output to file */
- int output = creat(*outfile, 0600);
- dup2(output, STDOUT_FILENO);
- if(mode & PIPE_STDERR)
- dup2(output, STDERR_FILENO);
- else if(errfile)
- dup2(creat(*errfile, 0600), STDERR_FILENO);
- }
-
- if(mode & PIPE_WRITE){ /* connect process input */
- close(oparentd);
- dup2(ichildd, STDIN_FILENO); /* tie stdin to pipe */
- close(ichildd);
- }
-
- if(mode & PIPE_READ){ /* connect process output */
- close(iparentd);
- dup2(ochildd, STDOUT_FILENO); /* tie std{out,err} to pipe */
- if(mode & PIPE_STDERR)
- dup2(ochildd, STDERR_FILENO);
- else if(errfile)
- dup2(creat(*errfile, 0600), STDERR_FILENO);
-
- close(ochildd);
- }
-
- if(mode & PIPE_NOSHELL){
- char **argv, **ap, *p, *cmd;
- size_t n;
-
- /* parse the arguments into a list */
- for(cmd = cpystr(command); *cmd && isspace(*cmd); cmd++)
- ; /* swallow leading ws */
-
- for(p = cmd, n = 2; *p; p++) /* count the args */
- if(isspace(*p) && *(p+1) && !isspace(*(p+1)))
- n++;
-
- argv = ap = (char **)fs_get(n * sizeof(char *));
- memset(argv, 0, n * sizeof(char *));
- for(p = cmd; *p && !isspace(*p); p++)
- ;
-
- if(*p) /* tie off command name */
- *p++ = '\0';
-
- *ap++ = cpystr(cmd);
- while(*p){ /* collect args */
- while(*p && isspace(*p))
- *p++ = '\0';
-
- *ap++ = (*p) ? p : NULL;
- while(*p && !isspace(*p))
- p++;
- }
-
- execvp(cmd, argv);
- }
- else{
- if(mode & PIPE_USER){
- char *env, *sh;
- if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
- shell = sh + 1;
- strcpy(shellpath, env);
- }
- else{
- shell = "csh";
- strcpy(shellpath, "/bin/csh");
- }
- }
- else{
- shell = "sh";
- strcpy(shellpath, "/bin/sh");
- }
-
- execl(shellpath, shell, command ? "-c" : 0, command, 0);
- }
-
- fprintf(stderr, "Can't exec %s\nReason: %s",
- command, error_description(errno));
- _exit(-1);
- }
-
- if(syspipe->pid > 0){
- syspipe->isig = signal(SIGINT, SIG_IGN); /* Reset handlers to make */
- syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to */
- syspipe->hsig = signal(SIGHUP, SIG_IGN); /* a premature end... */
-
- if(mode & PIPE_WRITE){
- close(ichildd);
- syspipe->ofilep = fdopen(oparentd, "w");
- }
-
- if(mode & PIPE_READ){
- close(ochildd);
- syspipe->ifilep = fdopen(iparentd, "r");
- }
-
- dprint(5, (debugfile, "PID: %d, COMMAND: %s\n",syspipe->pid,command));
- }
- else{
- if(mode & (PIPE_WRITE | PIPE_READ)){
- if(mode & PIPE_WRITE){
- close(oparentd);
- close(ichildd);
- }
-
- if(mode & PIPE_READ){
- close(iparentd);
- close(ochildd);
- }
- }
- else{
- ClearScreen();
- ps_global->mangled_screen = 1;
- }
-
- if(mode & PIPE_RESET)
- Raw(1);
-
- #ifdef SIGCHLD
- (void) signal(SIGCHLD, SIG_DFL);
- #endif
-
- q_status_message1(SM_ORDER,3,3, "Error executing external command: %s",
- error_description(errno));
- fs_give((void **)&syspipe);
- if(outfile)
- fs_give((void **)outfile);
-
- dprint(1, (debugfile, "CAN'T FORK FOR COMMAND: %s\n", command));
- }
-
- return(syspipe);
- }
-
-
-
- /*----------------------------------------------------------------------
- Close pipe previously allocated and wait for child's death
-
- Args: syspipe -- address of pointer to struct returned by open_system_pipe
- Returns: returns exit status of child or -1 if invalid syspipe
- ----*/
- int
- close_system_pipe(syspipe)
- PIPE_S **syspipe;
- {
- WaitType stat;
- int status;
-
- if(!syspipe || !*syspipe)
- return(-1);
-
- if((*syspipe)->ofilep)
- fclose((*syspipe)->ofilep);
-
- if((*syspipe)->ifilep)
- fclose((*syspipe)->ifilep);
-
- #ifdef SIGCHLD
- {
- SigType (*alarm_sig)();
- int old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
-
- /*
- * remember the current SIGALRM handler, and make sure it's
- * installed when we're finished just in case the longjmp
- * out of the SIGCHLD handler caused sleep() to lose it.
- * Don't pay any attention to that man behind the curtain.
- */
- alarm_sig = signal(SIGALRM, SIG_IGN);
- (void) signal(SIGALRM, alarm_sig);
- F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
- ps_global->noshow_timeout = 1;
- while(!child_signalled){
- new_mail(0, 2, 0); /* wake up and prod server */
-
- if(!child_signalled){
- if(setjmp(child_state) == 0){
- child_jump = 1; /* prepare to wake up */
- sleep(600); /* give it 5mins to happend */
- }
- #ifdef POSIX_SIGNALS
- else
- sigrelse(SIGCHLD); /* unblock signal after longjmp */
- #endif
- }
-
- child_jump = 0;
- }
-
- ps_global->noshow_timeout = 0;
- F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
- (void) signal(SIGALRM, alarm_sig);
- (void) signal(SIGCHLD, SIG_DFL);
- }
- #endif
-
- /*
- * Call c-client's pid reaper to wait() on the demise of our child,
- * then fish out its exit status...
- */
- grim_pid_reap_status((*syspipe)->pid, 0, &stat);
- status = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
-
- /*
- * restore original handlers...
- */
- (void)signal(SIGINT, (*syspipe)->isig);
- (void)signal(SIGHUP, (*syspipe)->hsig);
- (void)signal(SIGQUIT, (*syspipe)->qsig);
-
- if((*syspipe)->mode & PIPE_RESET) /* restore our tty modes */
- Raw(1);
-
- if(!((*syspipe)->mode & (PIPE_WRITE | PIPE_READ))){
- ClearScreen(); /* No I/O to forked child */
- ps_global->mangled_screen = 1;
- }
-
- fs_give((void **)syspipe);
- return(status);
- }
-